package org.opencare.plugins.gdacs;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.LinkedList;

import javax.jms.JMSException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.Resource;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

public class PollerObject implements ApplicationContextAware {

	private ApplicationContext applicationContext;

	/** Logger for this class and subclasses */
	protected final Log logger = LogFactory.getLog(getClass());
	private Publisher publisherTarget = null;
	private ResourcePoller pollerTarget = null;
	private OpenCARETransformer transformerTarget = null;
	private String url = null;
	private Boolean isTest = new Boolean(false);
	private LinkedList<String> hashList = new LinkedList<String>();
	private String testFile;

	public void setTestFile(String testFile) {
		this.testFile = testFile;
	}

	public void setIsTest(String isTest) {
		this.isTest = new Boolean(isTest);
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public void setPollerTarget(ResourcePoller pollerTarget) {
		this.pollerTarget = pollerTarget;
	}

	public void setTransformerTarget(OpenCARETransformer transformerTarget) {
		this.transformerTarget = transformerTarget;
	}

	public void setPublisherTarget(TopicPublisher publisherTarget) {
		this.publisherTarget = publisherTarget;
	}

	public void feed() {

		String gdacsMessage = null;
		StringBuffer contents = new StringBuffer();
		// declared here only to make visible to finally clause
		BufferedReader input = null;
		String abTestFile = null;

		// do the actual work
		logger
				.info("------------------------- Start poll -------------------------");

		try {

			Resource rs = applicationContext.getResource(testFile);
			abTestFile = rs.getFile().getAbsolutePath();

			if (this.isTest.booleanValue()) { // Test message
				logger
						.info("------------------------- Test message -------------------------");

				// use buffering, reading one line at a time
				// FileReader always assumes default encoding is OK!
				input = new BufferedReader(new FileReader(abTestFile));
				String line = null; // not declared within while loop
				/*
				 * readLine is a bit quirky : it returns the content of a line
				 * MINUS the newline. it returns null only for the END of the
				 * stream. it returns an empty String if two newlines appear in
				 * a row.
				 */
				while ((line = input.readLine()) != null) {
					contents.append(line);
					contents.append(System.getProperty("line.separator"));
				}

				gdacsMessage = contents.toString();
				logger
						.info("------------------------- End test message -------------------------");

			} else { // Poll from real resource
				gdacsMessage = pollerTarget.getResource(url);
			}

			if (gdacsMessage != null && !gdacsMessage.equalsIgnoreCase("")) {
				String edxlMessage = null;

				logger
						.debug("------------------------- GDACS message -------------------------");
				logger.debug(gdacsMessage);
				logger
						.debug("------------------------- End of GDACS message -------------------------");

				int numElement = countElement(gdacsMessage);
				logger.debug("Total number of CAP: " + numElement);

				if (numElement > 0 && !hasDuplicate(gdacsMessage)) {
					edxlMessage = transformerTarget.transform(gdacsMessage);
					logger.debug(edxlMessage);
					publisherTarget.publish(edxlMessage);
				} else {

					logger
							.info("number of CAP is 0 or duplicate message!, don't transform and don't send to JMS!");
				}

				edxlMessage = null;

			}
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (TransformerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (JMSException e) { // TODO Auto-generated catch block
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				if (input != null) {
					// flush and close both "input" and its underlying
					// FileReader
					input.close();
				}
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}

		logger
				.info("------------------------- End poll -------------------------");
	}

	// Count number of CAP element
	public int countElement(String data) {

		DocumentBuilder builder = null;
		Document doc = null;
		XPath xPath = null;
		NodeList items = null;

		try {
			builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			doc = builder.parse(new InputSource(new StringReader(data)));
			xPath = XPathFactory.newInstance().newXPath();

			String expression = "rss/channel/item";

			items = (NodeList) xPath.evaluate(expression, doc,
					XPathConstants.NODESET);

			for (int i = 0; i < items.getLength(); i++) {
				Node n = items.item(i);

				logger.debug(n.getNodeName());
			}

			logger.debug("Amount of <item> element: " + items.getLength());

		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (XPathExpressionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return items.getLength();
	}

	// Check for duplicate alert.
	public boolean hasDuplicate(String data) {

		DocumentBuilder builder = null;
		Document doc = null;
		XPath xPath = null;
		NodeList items = null;
		MessageDigest digest;
		Hex hex = new Hex();

		// store only 100 items id.
		if (hashList.size() > 100) {
			logger.debug("remove an element from list!");
			hashList.remove();
		}

		try {

			builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			doc = builder.parse(new InputSource(new StringReader(data)));
			xPath = XPathFactory.newInstance().newXPath();
			digest = MessageDigest.getInstance("MD5");

			String expression = "rss/channel/item/link";

			items = (NodeList) xPath.evaluate(expression, doc,
					XPathConstants.NODESET);

			for (int i = 0; i < items.getLength(); i++) {
				Node item = items.item(i);
				logger.debug("<link>: " + item.getTextContent());

				digest.update(item.getTextContent().getBytes());
				byte[] hash = digest.digest();

				String encoded = new String(hex.encode(hash));
				logger.debug("MD5(<link>): " + encoded);

				if (hashList.contains(encoded)) {
					logger.debug("Duplicate item occur!: " + encoded);
					if (isTest.booleanValue())
						return false;
					else
						return true;
				} else {
					logger.debug("New item, add hash of <link> to list");
					hashList.add(encoded);
				}
			}
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (XPathExpressionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return false;
	}

	@Override
	public void setApplicationContext(ApplicationContext ac)
			throws BeansException {
		// TODO Auto-generated method stub
		this.applicationContext = ac;
	}
}
